home *** CD-ROM | disk | FTP | other *** search
- /* flox.rexx - generates simulated flocking paths of a chosen number of
- followers tracking
- * a leader path
- *
- *
- * Program operation:
- *
- * Record the definition of a bspline path in Real3D and save it as an RPL
- script
- * Run this program
- * Give the name of the 'leader' bspline RPL script saved above
- * Specify:
- * total number of frames
- * number of followers
- * factors affecting the behaviour of each follower
- * output file to save generated RPL bspline paths to
- *
- * The program then generates paths that follow the leader's in a pattern that
- * attempts to simulate flocking behaviour.
- *
- * Revision History
- *
- * Date Who Reason
- * -------------------------------------
- * 25OCT94 DRO Original
- * 26OCT94 DRO Added user-definable weighting for when the follower
- re-calculates
- * its direction to the leader.
- *
- *
- */
-
- /* constants */
- TRUE=(1=1)
- FALSE=~TRUE
-
- /* Set precision */
- NUMERIC DIGITS 6
-
- /* Intro */
- SAY 'Flox v1.1'
- SAY '---------'
- SAY ''
- SAY 'This program takes a Real3D B-Spline defined in an RPL script file and
- uses it'
- SAY 'and a number of parameters to create RPL B-Spline definitions of paths
- that attempt'
- SAY 'to follow the leader in a flocking pattern.'
- SAY ''
- SAY 'The resulting B-Splines are written to a file, which should then be
- run from Real3D.'
- SAY ''
- SAY 'Note: you must use the macro recording feature of Real3D to define the
- leader B-Spline'
- SAY 'as follows:'
- SAY ''
- SAY '1. Select Project > Macros > Record to start recording'
- SAY '2. Define the B-Spline'
- SAY '3. Select Project > Macros > Record to stop recording'
- SAY '4. Optionally, select Project > Macros > Current to Named, and
- specify a filename'
- SAY ' under which to save the RPL. Alternatively, the macro deafaults
- to T:macro.rpl'
- SAY ''
- SAY 'I based it on a bit of thinking, which may prove to be hopelessly
- inadequate ;-)'
- SAY 'David Oxley, 25 October 1994, oxleyd@logica.co.uk'
- SAY ''
-
- leader_default="T:MACRO.RPL"
- /* Get name of leader RPL script file */
- SAY 'Enter leader RPL script filename, or Q to quit:
- [default='||leader_default||'] '
- PULL response
- IF response = "Q" THEN EXIT 10
- IF response = '' THEN response=leader_default
-
- /* try to open file */
- IF OPEN(leaderf,response,'READ') = FALSE THEN
- DO
- SAY 'Unable to open leader file "'||response||'".'
- EXIT 10
- END
-
- /* Now scan the leader file and extract the knot point x,y,z values and
- number of knots
- *
- * Sample RPL bspline macro script follows:
- *
- * 1 O_LOCK ( iLOCK_EXCL )
- *
- * -0.95 -1.00333 0.6
- * -0.779436 -0.405667 0.6
- * -0.812256 0.226 0.6
- * -0.031538 0.721667 0.6
- * 0.23841 -0.552667 0.6
- * 0.697897 0.389 0.6
- * 1.11 0.776667 0.6
- * 7 ( count )
- * 3 ( type )
- * 0 ( geom. flags )
- * 255 255 255 0 ( RGBA )
- * "line" ( name )
- * 65664 ( flags )
- * "CEND"
- * C_LINE DROP
- *
- * 0 O_LOCK ( iLOCK_REMOVE )
- *
- *
- */
-
- /* Skip first two lines */
- skip=READLN(leaderf)
- skip=READLN(leaderf)
-
- /* Now start reading the coords */
- read_complete=FALSE
- count=1
- DO UNTIL read_complete=TRUE
- line=READLN(leaderf)
- PARSE VAR line leader_x.count leader_y.count leader_z.count
- IF DATATYPE(leader_y.count) = 'CHAR' THEN
- read_complete=TRUE
- ELSE
- count=count+1
- END
-
- /* Assumption now is that the value in leader_x.count is the count of knots */
- leader_knots=leader_x.count
-
- /* We must evaluate an imaginary knot point after the last point in the
- leader's spline,
- * by simple linear extrapolation from the last and penultimate points
- * This point is necessary for calculating the aiming point for the
- follower as it nears
- * the end of the animation, and hence the end of the leader's spline.
- *
- * Conveniently, count is set to one more than the number of knot points.
- Notice, however,
- * that we don't include this last point in the leader_knots value.
- */
- penultimate=leader_knots-1
- leader_x.count=leader_x.leader_knots+(leader_x.leader_knots-leader_x.penulti
- mate)
- leader_y.count=leader_y.leader_knots+(leader_y.leader_knots-leader_y.penulti
- mate)
- leader_z.count=leader_z.leader_knots+(leader_z.leader_knots-leader_z.penulti
- mate)
-
- SAY 'Enter total number of frames for the animation, or Q to quit: '
- PULL response
- IF response = "Q" THEN EXIT 10
- total_frames=response
-
- /* Get an outfile that doesn't already exist. */
- file_exists=FALSE
- DO UNTIL response = "Q" | (response ~= "Q" & file_exists=FALSE)
- SAY 'Enter output RPL script filename, or Q to quit: '
- PULL response
- file_exists=EXISTS(response)
- IF file_exists THEN
- SAY 'File '||response||' already exists. Pick another.'
- END
- IF response = "Q" THEN EXIT 10
- outfile=response
-
- SAY 'Enter the number of followers, or Q to quit: '
- PULL response
- IF response = "Q" THEN EXIT 10
- num_followers=response
-
- /* choose default values for the characteristics */
- independence_default=(total_frames % leader_knots)+1
- accuracy_default=0.5
- offset=(ABS(leader_x.2-leader_x.1)+ABS(leader_y.2-leader_y.1)+ABS(leader_z.2
- -leader_z.1))/3
- proximity_default=offset/3
- freedom_default=1
-
- /* prop is the multiplication factor used to scale the direction vector
- when it is
- * re-targeted to the estimated position of the leader
- */
- prop=leader_knots/total_frames
-
- SAY 'Enter the distance between the leader and a follower that should be
- considered close'
- SAY 'enough that the two are touching, or Q to quit:
- [default='||proximity_default||'] '
- PULL response
- IF response = "Q" THEN EXIT 10
- IF response = '' THEN response=proximity_default
- proximity=response
-
- /* Now get the behaviour characteristics of each follower */
- DO i=1 TO num_followers
- SAY 'For follower '||i||', please enter the following values:'
- SAY ''
- SAY 'Independence: on average, how many frames this follower will
- continue on its own course'
- SAY 'for, before re-assessing its direction to the leader, or Q to quit:'
- SAY '(1=always track leader, >1=be more independent)
- [default='||independence_default||'] '
- PULL response
- IF response = "Q" THEN EXIT 10
- IF response = '' THEN response=independence_default
- IF response < 1 THEN response=1
- IF response > total_frames THEN response=total_frames+1
- independence.i=response
- SAY ''
- SAY 'Accuracy: on average, how good this follower is at judging its
- distance from the leader,'
- SAY 'or Q to quit: (0=inaccurate, 1=accurate)
- [default='||accuracy_default||'] '
- PULL response
- IF response = "Q" THEN EXIT 10
- IF response = '' THEN response=accuracy_default
- IF response < 0 THEN response=0
- IF response > 1 THEN response=1
- accuracy.i=response
- SAY ''
- SAY 'Freedom: a high value will, on average, cause the follower to over
- or undershoot the'
- SAY 'position of the leader. A low value will tend the follower to match
- the leader,'
- SAY 'or Q to quit: (0=conform, >0=be free) [default='||freedom_default||'] '
- PULL response
- IF response = "Q" THEN EXIT 10
- IF response = '' THEN response=freedom_default
- IF response < 0 THEN response=0
- IF response > 10 THEN response=10
- freedom.i=response
- SAY ''
-
- /* Now calculate where to position this follower initially. Pretty
- random, but based
- * on the average of the distance between the leader's first and second
- knot points.
- * Same sort of thing to determine the initial direction
- */
- pos_fol_x.i=leader_x.1+(RANDU()-0.5)*offset
- pos_fol_y.i=leader_y.1+(RANDU()-0.5)*offset
- pos_fol_z.i=leader_z.1+(RANDU()-0.5)*offset
- dir_fol_x.i=(RANDU()-0.5)*offset
- dir_fol_y.i=(RANDU()-0.5)*offset
- dir_fol_z.i=(RANDU()-0.5)*offset
- END
-
- IF OPEN(outf,outfile,'WRITE') = FALSE THEN
- DO
- SAY 'Unable to open output file "'||outfile||'".'
- EXIT 10
- END
-
- /* We have all the values we need, let's generate the followers' splines! */
-
- DO i=1 TO num_followers
- /* Keep the user informed */
- SAY 'Follower '||i||', writing spline information...'
-
- /* RPL object locking code at the start of each new object */
- CALL WRITELN(outf,'1 O_LOCK')
- CALL WRITELN(outf,'')
- CALL WRITELN(outf,pos_fol_x.i||' 'pos_fol_y.i||' 'pos_fol_z.i)
-
- curr_frame=0
-
- /* knot_count will be written to the script file after the coords */
- knot_count=1
-
- /* ind_timer is based on the independence value for this follower, and
- determines, for
- * this spline segment, how long before the follower re-assesses its position relative
- * to the leader (modulo arithmetic gives the integer value)
- */
- ind_timer=(independence.i*judgement(accuracy.i))%1
-
- DO UNTIL curr_frame >= total_frames
- pos_fol_x.i=pos_fol_x.i+dir_fol_x.i
- pos_fol_y.i=pos_fol_y.i+dir_fol_y.i
- pos_fol_z.i=pos_fol_z.i+dir_fol_z.i
- ind_timer=ind_timer-1
- IF (ind_timer<1) | (total_frames-curr_frame)<=1 THEN
- DO
- /* The timer has expired, or we are on the last frame of the
- animation, so we must
- * write the current position to the script file.
- */
- CALL WRITELN(outf,pos_fol_x.i||' 'pos_fol_y.i||' 'pos_fol_z.i)
- knot_count=knot_count+1
- ind_timer=independence.i*judgement(accuracy.i)
-
- /* We must now reset the direction vector for this follower, so that
- it tracks,
- * closely or loosely, the current position of the leader.
- *
- * seg_pos tells us approximately how far along the current line
- segment the leader is
- */
- seg_pos=curr_frame*leader_knots/total_frames
-
- /* use // (remainder) and % (modulo) to get the fractional and
- integer parts of seg_pos */
- frac=seg_pos//1
- est_knot=1+(seg_pos%1)
- next_knot=est_knot+1
-
- /* Now work out the estimated position of the leader, where this
- follower will aim */
- est_leader_x=leader_x.est_knot+frac*(leader_x.next_knot-leader_x.est_knot)
- est_leader_y=leader_y.est_knot+frac*(leader_y.next_knot-leader_y.est_knot)
- est_leader_z=leader_z.est_knot+frac*(leader_z.next_knot-leader_z.est_knot)
-
-
- /* Calculate the new direction vector based on the estimated position
- of the leader
- * as seen from this follower's current position. Scale it according
- to the average
- * number of frames per line segment, and according to the accuracy
- of this follower.
- *
- * weight is a factor added to the new direction calculation that
- determines how
- * well the follower will eventually match the leader's position: how
- "free" it is.
- * It is a random value based on freedom.i, centred on 0.
- */
- weight=(freedom.i*RANDU())-freedom.i/2
-
- dir_fol_x.i=prop*(weight+(est_leader_x-pos_fol_x.i)-proximity)*judgement(acc
- uracy.i)
-
- dir_fol_y.i=prop*(weight+(est_leader_y-pos_fol_y.i)-proximity)*judgement(acc
- uracy.i)
-
- dir_fol_z.i=prop*(weight+(est_leader_z-pos_fol_z.i)-proximity)*judgement(acc
- uracy.i)
- END /* IF ind_timer<1 */
-
- curr_frame=curr_frame+1
- END /* DO UNTIL curr_frame >= total_frames */
-
- /* Now append the spline information to the script file */
- CALL WRITELN(outf,knot_count||' ( count )')
- CALL WRITELN(outf,'3 ( type )')
- CALL WRITELN(outf,'0 ( geom. flags )')
- CALL WRITELN(outf,'255 255 255 0 ( RGBA )')
- CALL WRITELN(outf,'"follower.'||i||'" ( name )')
- CALL WRITELN(outf,'65664 ( flags )')
- CALL WRITELN(outf,'"CEND"')
- CALL WRITELN(outf,'C_LINE DROP')
- CALL WRITELN(outf,'')
- CALL WRITELN(outf,'0 O_LOCK ( iLOCK_REMOVE )')
-
- SAY 'done.'
-
- END /* DO i=1 TO num_followers */
-
- IF CLOSE(outf) = FALSE THEN
- DO
- SAY 'Unable to close output file "'||outfile||'".'
- EXIT 10
- END
-
- SAY 'All done. File '||outfile||' contains the generated spline(s).'
-
- EXIT 0
-
- /*
- * Judgement is a weighted random function that returns a number in the
- range 0.5 to 1.5,
- * the weighting being controlled by 'accuracy'. Accuracy of 1 always
- returns 1;
- * Accuracy of 0 returns a random value in the 0.5-1.5 range.
- */
- judgement: PROCEDURE
- ARG accuracy
-
- retval=(1+((1-accuracy)*(RANDU()-0.5)))
- RETURN retval
-
-
-